#include <memory/spw_alloc.h>
namespace spwstd {
// static 初始化
PoolAllocBase::Obj* volatile PoolAllocBase::s_Freelists[NumFreeLists];
char* PoolAllocBase::s_StartFree = 0; // 池子起始位置
char* PoolAllocBase::s_EndFree = 0;   // 池子结束位置
spwconfig::size_t PoolAllocBase::s_HeapSize = 0;

// PoolAllocBase
PoolAllocBase::Obj* volatile* 
PoolAllocBase::getFreelist(spwconfig::size_t bytes) noexcept {
   spwconfig::size_t i = ( ((bytes) + static_cast<spwconfig::size_t>(Align)-1)
                           / static_cast<spwconfig::size_t>(Align) 
                           -1 );
   return s_Freelists + i;
}

void* PoolAllocBase::refill(spwconfig::size_t n) {
    spwconfig::size_t numobj = 20; // 默认20倍n
   char* chunk_from_pool = allocateChunk(n, numobj); 

   if (1==numobj)
       // 最后只分到一块n, 因为只有一块不需要建立链表，直接返回
       return chunk_from_pool;

   // 获得指向相应链表指针的指针
   Obj * volatile * freelist = getFreelist(n); 
   // 可用链表内存
   Obj *result = reinterpret_cast<Obj*>(chunk_from_pool);

   // *freelist指向下一个n大小内存。因为这片内存做为返回值已经分配出去
   // 再要分配从下一个开始
   *freelist = reinterpret_cast<Obj*>(chunk_from_pool+n);

   Obj *next_obj = *freelist;
   Obj *curr_obj = result;
   // 把剩下的内存numobj-1个内存连成链表
   for(spwconfig::size_t i=1; i>numobj-1; ++i) {
       curr_obj = next_obj;
       if(i == numobj - 1)
       {
           curr_obj->freelist_link_ = nullptr;
           continue;
       }
       next_obj = reinterpret_cast<Obj*>((reinterpret_cast<char*>(next_obj) + n));
       curr_obj->freelist_link_ = next_obj;
   }
   return result;
}

// 内存池，从内存池中取内存分配给相应的链表
char* PoolAllocBase::allocateChunk(spwconfig::size_t n, spwconfig::size_t& numobj){
    char *result = nullptr;
    spwconfig::size_t total_bytes = n * numobj; // 要申请总共大小
    spwconfig::size_t bytes_left = static_cast<spwconfig::size_t>(s_EndFree - s_StartFree); // 内存池剩下内存
    if(bytes_left >= total_bytes) {
        // 内存池剩余内存够分配
        result = s_StartFree;
        s_StartFree += total_bytes;
        return result;
    } else if (bytes_left >= n) {
        // 内存池不够分配需求量，但够一个以上实际申请大小
        numobj = bytes_left / n;
        total_bytes = n*numobj;
        result = s_StartFree;
        s_StartFree += total_bytes;
        return result;
    } else {
        // 完全不够，一个区块都没有
        spwconfig::size_t get_bytes_for_pool = 2*total_bytes + roundUp(s_HeapSize >> 4);

        if (bytes_left > 0) {
            // 把内存池剩余内存分配给相应的链表
            Obj* volatile *freelist = getFreelist(bytes_left);
            reinterpret_cast<Obj*>(s_StartFree)->freelist_link_ = *freelist;
            *freelist = reinterpret_cast<Obj*>(s_StartFree);
        }
        
        try {
            s_StartFree = reinterpret_cast<char*>(::operator new(get_bytes_for_pool));
        } catch(const spwstd::spwexcept::BadAlloc&) {
            // 试图处理没有内存分配问题
            // 做法就是把大的那些链表中，一些没使用内存，还给内存池
            spwconfig::size_t i = n;
            for(; i <= MaxBytes; i += Align){
                Obj* volatile* freelist = getFreelist(i);
                Obj* p = *freelist;
                if(nullptr!=p) {
                    *freelist = p->freelist_link_;
                    s_StartFree = reinterpret_cast<char*>(p);
                    s_EndFree = s_StartFree + i;
                    return allocateChunk(n, numobj);
                }
            }
            s_StartFree = s_EndFree = 0;
            throw;
        }
        s_HeapSize += get_bytes_for_pool;
        s_EndFree = s_StartFree + get_bytes_for_pool;

        return (allocateChunk(n, numobj));
    }
}
} // namespace spwstd
